﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Servicios.Business.Enumerators;
using Servicios.Data.DataBases.Biblioteca;

namespace Servicios.Business.Modulo.MotorBusqueda
{
    /// <summary>
    /// Representa el conjunto de resultados de una busqueda
    /// </summary>
    //[DebuggerDisplay("Count = {Count}/Autor = {Autor}/Material = {MaterialBibliografico}/Editorial = {Editorial}")]
    public class ResultadoBusqueda : List<ResultadoBusqueda>, IResultadoBusqueda
    {
        #region Miembros publicos
        public readonly string Pregunta;
        public readonly TipoBusqueda TipoBusqueda;
        #endregion

        #region Propriedades
        /// <summary>
        /// Expone la informacion del Autor
        /// </summary>
        public AutorDTO Autor { get; set; }
        /// <summary>
        /// Expone la informacion de la Editorial 
        /// </summary>
        public EditorialDTO Editorial { get; set; }
        /// <summary>
        /// Expone la informacion del Material Bibliografico
        /// </summary>
        public MaterialBibliograficoDTO MaterialBibliografico { get; set; }
        /// <summary>
        /// Expone la informacion del Pais
        /// </summary>
        public PaisDTO Pais { get; set; }
        /// <summary>
        /// Expone la informacion del Idioma
        /// </summary>
        public IdiomaDTO Idioma { get; set; }
        /// <summary>
        /// Expone la cantidad de material bibliografico
        /// </summary>
        public int Cantidad { get; set; }
        /// <summary>
        /// Expone el tipo de material Bibliografico
        /// </summary>
        public TipoMaterialBibliograficoDTO TipoMaterialBibliografico { get; set; }
        /// <summary>
        /// Expone la existencia del material bibliografico
        /// </summary>
        public ExistenciasPorMatBib Existencia { get; set; }
        /// <summary>
        /// Tipo de prestamo
        /// </summary>
        public TipoPrestamoDTO TipoPrestamo { get; set; }
        #endregion

        /// <summary>
        /// Constructor por defecto
        /// </summary>
        public ResultadoBusqueda()
        { }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="pregunta">Pregunta escrita por el utilisador</param>
        /// <param name="tipoBusqueda">Tipo de busqueda</param>
        public ResultadoBusqueda(string pregunta, TipoBusqueda tipoBusqueda)
        {
            this.Pregunta = pregunta;
            if (Enum.IsDefined(typeof(TipoBusqueda), tipoBusqueda))
                this.TipoBusqueda = tipoBusqueda;
            else
                throw new ArgumentException("El tipo de busqueda definido no existe");
        }
        /// <summary>
        /// envia el resultado de la busqueda
        /// </summary>
        public ResultadoBusqueda GetResultadoBusqueda()
        {
            switch (this.TipoBusqueda)
            {
                case TipoBusqueda.Título:
                    return ResultadoPorTitulo();
                case TipoBusqueda.Autor:
                    return ResultadoPorAutor();
                case TipoBusqueda.Tema:
                    return ResultadoPorTema();
                default:
                    break;
            }
            throw new ArgumentException("Resultado no previsto para el tipo de vista especificado");
        }
        /// <summary>
        /// Envia el resultado de la busqueda
        /// </summary>
        public ResultadoBusqueda GetResultadoBusqueda(int id)
        {
            using (var be = new BibliotecaContext())
            {
                var query = from mb in be.MaterialBibliograficoSet
                            join autorMaterialBibliografico in be.MatBibPorAutorSet
                                on mb.ID equals autorMaterialBibliografico.MatBibID
                            join editorial in be.EditorialSet
                                on mb.Bib_Editorial.ID equals editorial.ID into editorial
                            join tipoMaterial in be.TipoMaterialBibliograficoSet
                                on mb.Bib_TipoMaterialBibliografico.ID equals tipoMaterial.ID
                            join autor in be.AutorSet
                                on autorMaterialBibliografico.AutorID equals autor.ID
                            join p in be.PaisSet
                                on mb.Bib_Pais.ID equals p.ID into pais
                            join i in be.IdiomaSet
                                on mb.Bib_Idioma.ID equals i.ID into idioma
                            join exists in be.ExistenciasPorMatBibSet
                                on mb.ID equals exists.Bib_MaterialBibliografico.ID into Existencias
                            orderby autor.ID
                            where
                                            mb.ID == id &&
                                            autorMaterialBibliografico.AutorID == autor.ID &&
                                            autorMaterialBibliografico.MatBibID == mb.ID

                            group new
                            {
                                Autor = autor.Apellidos + ", " + autor.Nombres
                            }
                                by new
                                {
                                    ID = mb.ID,
                                    Titulo = mb.Titulo,
                                    Clasificacion = mb.Clasificacion,
                                    TipoMaterialBibliografico = tipoMaterial.Nombre,
                                    Editorial = editorial.FirstOrDefault(),
                                    Idioma = idioma.FirstOrDefault(),
                                    Pais = pais.FirstOrDefault(),
                                    NumeroExistencias = Existencias.Count(),
                                }
                                into result
                                select result;
                ResultadoBusqueda nodo;
                string nomAutores = string.Empty;

                foreach (var m in query)
                {

                    nodo = new ResultadoBusqueda()
                               {
                                   MaterialBibliografico = new MaterialBibliograficoDTO()
                                                               {
                                                                   ID = m.Key.ID,
                                                                   Titulo = m.Key.Titulo,
                                                                   Clasificacion = m.Key.Clasificacion
                                                               },
                                   TipoMaterialBibliografico = new TipoMaterialBibliograficoDTO()
                                   {
                                       Nombre = m.Key.TipoMaterialBibliografico
                                   },
                                   Editorial = new EditorialDTO()
                                   {
                                       Nombre = m.Key.Editorial == null ? string.Empty : m.Key.Editorial.Nombre
                                   },
                                   Pais = new PaisDTO()
                                   {
                                       Nombre = m.Key.Pais == null ? string.Empty : m.Key.Pais.Nombre
                                   },
                                   Idioma = new IdiomaDTO()
                                   {
                                       Nombre = m.Key.Idioma == null ? string.Empty : m.Key.Idioma.Nombre
                                   }
                               };
                    //Garcia Marquez, Gabriel/Vargas Llosa, Mario
                    foreach (var au in m)
                    {
                        nomAutores = au.Autor + " / " + nomAutores;
                    }


                    nomAutores = nomAutores.Substring(0, nomAutores.Length - 2);
                    nodo.Autor = new AutorDTO() { Nombres = nomAutores };
                    nodo.Cantidad = m.Key.NumeroExistencias;

                    this.Add(nodo);
                }
                return this;
            }

        }

        public ResultadoBusqueda GetExistencia(int id)
        {
            using (var be = new BibliotecaContext())
            {
                var query = from exists in be.ExistenciasPorMatBibSet
                            join mb in be.MaterialBibliograficoSet on exists.Bib_MaterialBibliografico.ID equals mb.ID
                            join tp in be.TipoPrestamoSet on exists.Bib_TipoPrestamo.ID equals tp.ID
                            where
                                mb.ID == id &&
                                exists.Bib_MaterialBibliografico.ID == mb.ID &&
                                exists.Habilitado == true
                            select new
                                       {
                                           MaterialBibliografico = mb,
                                           Existencia = exists,
                                           TipoPrestamo = tp
                                       };

                this.Clear();
                foreach (var item in query)
                {
                    this.Add(new ResultadoBusqueda()
                                 {
                                     MaterialBibliografico = item.MaterialBibliografico,
                                     Existencia = item.Existencia,
                                     TipoPrestamo = item.TipoPrestamo
                                 });
                }

                Cantidad = query.Count();


                return this;
            }
        }

        public ResultadoBusqueda GetExistencia(int id, int existenciaId)
        {
            return GetExistencia(id).First(e => e.Existencia.ID == existenciaId);
        }

        public ResultadoBusqueda ObtenerAutores(string prefixText, int count)
        {
            using (var bib = new BibliotecaContext())
            {
                var query = (from au in bib.AutorSet
                             where au.Apellidos.ToLower().StartsWith(prefixText.ToLower())
                                  || au.Nombres.ToLower().StartsWith(prefixText.ToLower())
                             select au).Take(count).Distinct();
                Cantidad = query.Count();
                foreach (var dto in query)
                {
                    this.Add(new ResultadoBusqueda { Autor = dto });
                }
                return this;
            }
        }


        private ResultadoBusqueda ResultadoPorTema()
        {
            using (var bibliotecaEntity = new BibliotecaContext())
            {
                var query = from mb in bibliotecaEntity.MaterialBibliograficoSet
                            join asmb in bibliotecaEntity.MatBibPorAutorSet on mb.ID equals asmb.MatBibID
                            join e in bibliotecaEntity.EditorialSet on mb.Bib_Editorial.ID equals e.ID
                            join tipoMaterial in bibliotecaEntity.TipoMaterialBibliograficoSet on
                                mb.Bib_TipoMaterialBibliografico.ID equals tipoMaterial.ID
                            join a in bibliotecaEntity.AutorSet on asmb.AutorID equals a.ID
                            orderby a.ID
                            where
                                mb.Titulo.Contains(this.Pregunta) &&
                                asmb.AutorID == a.ID &&
                                asmb.MatBibID == mb.ID
                            group new
                            {
                                Autor = a.Apellidos + ", " + a.Nombres
                            }
                                by new
                                {
                                    ID = mb.ID,
                                    Titulo = mb.Titulo,
                                    Clasificacion = mb.Clasificacion,
                                    AnioPublicacion = mb.AnioPublicacion,
                                    TipoMaterialBibliografico = tipoMaterial.Nombre,
                                    Editorial = e.Nombre,
                                }
                                into result
                                orderby result.Key.AnioPublicacion descending 
                                select result;

                ResultadoBusqueda nodo;
                string nomAutores = string.Empty;

                foreach (var m in query)
                {

                    nodo = new ResultadoBusqueda()
                    {
                        MaterialBibliografico = new MaterialBibliograficoDTO()
                        {
                            ID = m.Key.ID,
                            Titulo = m.Key.Titulo,
                            Clasificacion = m.Key.Clasificacion,
                            Descripcion = m.Key.TipoMaterialBibliografico,
                            AnioPublicacion = m.Key.AnioPublicacion
                        },
                        TipoMaterialBibliografico = new TipoMaterialBibliograficoDTO()
                        {
                            Nombre = m.Key.TipoMaterialBibliografico
                        },
                        Editorial = new EditorialDTO() { Nombre = m.Key.Editorial }
                    };
                    //Garcia Marquez, Gabriel/Vargas Llosa, Mario
                    foreach (var au in m)
                    {
                        nomAutores = au.Autor + " / " + nomAutores;
                    }


                    nomAutores = nomAutores.Substring(0, nomAutores.Length - 2);
                    nodo.Autor = new AutorDTO() { Nombres = nomAutores };
                    nodo.Cantidad = query.Count();

                    this.Add(nodo);
                    nomAutores = string.Empty;
                }
                return this;
            }
        }
        private ResultadoBusqueda ResultadoPorAutor()
        {
            using (var be = new BibliotecaContext())
            {

                var query = (from mb in be.MaterialBibliograficoSet
                             join aus in be.MatBibPorAutorSet on mb.ID equals aus.MatBibID

                             join ats in be.AutorSet on aus.AutorID equals ats.ID into Autores
                             from a in Autores
                             orderby a.ID

                             join tipoMB in be.TipoMaterialBibliograficoSet on mb.Bib_TipoMaterialBibliografico.ID equals
                                 tipoMB.ID
                             join exists in be.ExistenciasPorMatBibSet on mb.ID equals
                                 exists.Bib_MaterialBibliografico.ID into Existencias
                             join ed in be.EditorialSet on mb.Bib_Editorial.ID equals ed.ID into Editorial

                             

                             where
                                 (a.Nombres.Contains(Pregunta) || a.Apellidos.Contains(Pregunta)) &&
                                 aus.AutorID == a.ID &&
                                 aus.MatBibID == mb.ID
                             group new { Autor = a.Apellidos + ", " + a.Nombres }
                                 by new
                                        {
                                            ID = mb.ID,
                                            Titulo = mb.Titulo,
                                            Clasificacion = mb.Clasificacion,
                                            TipoMaterialBibliografico = tipoMB.Nombre,
                                            AnioPublicacion = mb.AnioPublicacion,
                                            NumeroExistencias = Existencias.Count(),
                                            Editorial = Editorial.FirstOrDefault()
                                        }
                                 into result
                                 orderby result.Key.AnioPublicacion descending 

                                 select result).ToList();
                ResultadoBusqueda nodo;
                string nomAutores = string.Empty;

                foreach (var m in query)
                {
                    nodo = new ResultadoBusqueda()
                    {
                        MaterialBibliografico = new MaterialBibliograficoDTO()
                        {
                            ID = m.Key.ID,
                            Titulo = m.Key.Titulo,
                            Clasificacion = m.Key.Clasificacion,
                            Descripcion = m.Key.TipoMaterialBibliografico,
                            AnioPublicacion = m.Key.AnioPublicacion
                        },
                        TipoMaterialBibliografico = new TipoMaterialBibliograficoDTO()
                        {
                            Nombre = m.Key.TipoMaterialBibliografico
                        },
                        Editorial = new EditorialDTO()
                        {
                            Nombre = m.Key.Editorial == null ? string.Empty : m.Key.Editorial.Nombre
                        }
                    };
                    //Garcia Marquez, Gabriel/Vargas Llosa, Mario
                    foreach (var au in m)
                    {
                        nomAutores = au.Autor + " / " + nomAutores;
                    }

                    nomAutores = nomAutores.Substring(0, nomAutores.Length - 2);
                    nodo.Autor = new AutorDTO() { Nombres = nomAutores };
                    nodo.Cantidad = m.Key.NumeroExistencias;

                    this.Add(nodo);
                    nomAutores = string.Empty;
                }
                return this;
            }
        }
        private ResultadoBusqueda ResultadoPorTitulo()
        {
            using (var be = new BibliotecaContext())
            {
                var query = from mb in be.MaterialBibliograficoSet
                            join aus in be.MatBibPorAutorSet on mb.ID equals aus.MatBibID
                            join a in be.AutorSet on aus.AutorID equals a.ID
                            orderby a.ID
                            join tipoMB in be.TipoMaterialBibliograficoSet on mb.Bib_TipoMaterialBibliografico.ID equals
                                tipoMB.ID
                            join exists in be.ExistenciasPorMatBibSet on mb.ID equals exists.Bib_MaterialBibliografico.ID
                                into existencias
                            join ed in be.EditorialSet on mb.Bib_Editorial.ID equals ed.ID into Editorial

                            

                            where mb.Titulo.Contains(Pregunta) &&
                                  aus.AutorID == a.ID &&
                                  aus.MatBibID == mb.ID
                            group new { Autor = a.Apellidos + "," + a.Nombres }
                                by new
                                       {
                                           ID = mb.ID,
                                           Titulo = mb.Titulo,
                                           Clasificacion = mb.Clasificacion,
                                           TipoMaterialBibliografico = tipoMB.Nombre,
                                           AnioPublicacion = mb.AnioPublicacion,
                                           NumeroExistencias = existencias.Count(),
                                           Editorial = Editorial.FirstOrDefault()
                                       }
                                into result

                                orderby result.Key.AnioPublicacion descending 
                                select result;

                ResultadoBusqueda nodo;
                string nomAutores = string.Empty;

                foreach (var m in query)
                {
                    nodo = new ResultadoBusqueda()
                    {
                        MaterialBibliografico = new MaterialBibliograficoDTO()
                        {
                            ID = m.Key.ID,
                            Titulo = m.Key.Titulo,
                            Clasificacion = m.Key.Clasificacion,
                            Descripcion = m.Key.TipoMaterialBibliografico,
                            AnioPublicacion = m.Key.AnioPublicacion
                        },
                        TipoMaterialBibliografico = new TipoMaterialBibliograficoDTO()
                        {
                            Nombre = m.Key.TipoMaterialBibliografico
                        },
                        Editorial = new EditorialDTO()
                        {
                            Nombre = m.Key.Editorial == null ? string.Empty : m.Key.Editorial.Nombre
                        }
                    };
                    //Garcia Marquez, Gabriel/Vargas Llosa, Mario
                    foreach (var au in m)
                    {
                        nomAutores = au.Autor + " / " + nomAutores;
                    }

                    nomAutores = nomAutores.Substring(0, nomAutores.Length - 2);
                    nodo.Autor = new AutorDTO() { Nombres = nomAutores };
                    nodo.Cantidad = m.Key.NumeroExistencias;

                    this.Add(nodo);
                    nomAutores = string.Empty;
                }
                return this;
            }
        }

    }
}
